// Graph Engine
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.md file in the project root for full license information.
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Trinity.Storage
{
    public interface IStructDescriptor : ITypeDescriptor, IAttributeCollection
    {

    }


    /// <summary>
    /// Provides metadata associated with a cell defined in TSL.
    /// </summary>
    public interface ICellDescriptor : IStructDescriptor
    {
        /// <summary>
        /// Enumerates the names of the fields of a cell. Note that, calling this method directly
        /// on a cell, an accessor, or their generic counterparts, will return the names of all available fields.
        /// The available fields of a particular cell include the fields that are not marked optional and the optional fields that have value(s).
        /// However, if the cell descriptor is obtained from the <c>Schema</c> class (generated from <c>TSL</c>),
        /// it returns the names of all the fields defined in the TSL, statically. 
        /// </summary>
        /// <returns>A collection of field names.</returns>
        IEnumerable<string> GetFieldNames();

        /// <summary>
        /// Gets the attributes associated with a field.
        /// </summary>
        /// <param name="fieldName">The name of the target field.</param>
        /// <returns>A collection of attributes associated with the field.</returns>
        IAttributeCollection GetFieldAttributes(string fieldName);

        /// <summary>
        /// Gets a collection of descriptors of the fields specified in TSL.
        /// </summary>
        /// <returns>A collection of field descriptors.</returns>
        IEnumerable<IFieldDescriptor> GetFieldDescriptors();

        //TODO move these to IStructDescriptor

        /// <summary>
        /// Gets an unsigned 16-bit integer representing the type of the cell. 
        /// The value can be converted from/to the enum <c>CellType</c> generated by TSL.
        /// </summary>
        ushort CellType { get; }
    }

    /// <summary>
    /// Exposes a group of generic methods to manipulate a cell. All cells defined in TSL implements <c>ICell</c>.
    /// </summary>
    public interface ICell : ICellDescriptor
    {
        /// <summary>
        /// Get the value of the specified cell field.
        /// </summary>
        /// <typeparam name="T">
        /// The desired type that the field is supposed 
        /// to be interpreted as. Automatic type casting 
        /// will be attempted if the desired type cannot be  
        /// implicitly converted from the type of the field.
        /// </typeparam>
        /// <param name="fieldName">The name of the target field.</param>
        /// <returns>The value of the field.</returns>
        T GetField<T>(string fieldName);

        /// <summary>
        /// Set the specified value to the specified cell field.
        /// </summary>
        /// <typeparam name="T">
        /// The type of the value.
        /// </typeparam>
        /// <param name="fieldName">The name of the target field.</param>
        /// <param name="value">
        /// The value of the field. Automatic type casting 
        /// will be attempted if the desired type cannot be
        /// implicitly converted from the type of the field.
        /// </param>
        void SetField<T>(string fieldName, T value);

        /// <summary>
        /// Tells if a field with the given name exists in the current cell.
        /// </summary>
        /// <param name="fieldName">The name of the field.</param>
        /// <returns>The existence of the field.</returns>
        bool ContainsField(string fieldName);

        /// <summary>
        /// Append <paramref name="value"/> to the target field. Note that if the target field
        /// is not appendable(string or list), calling this method is equivalent to calling <see cref="Trinity.Storage.ICell.SetField{T}(string, T)"/>.
        /// </summary>
        /// <typeparam name="T">
        /// The type of the value.
        /// </typeparam>
        /// <param name="fieldName">The name of the target field.</param>
        /// <param name="value">The value to be appended. 
        /// If the value is incompatible with the element 
        /// type of the field, automatic type casting will be attempted.
        /// </param>
        void AppendToField<T>(string fieldName, T value);

        /// <summary>
        /// The 64-bit id of the cell.
        /// </summary>
        long CellId { get; set; }

        /// <summary>
        /// Enumerate values of type T from the specified cell field.
        /// </summary>
        /// <typeparam name="T">
        /// The desired type that the elements are supposed
        /// to be enumerated as. Automatic type casting 
        /// will be attempted if the desired type cannot be  
        /// implicitly converted from the type of the actual element.
        /// If the field is not enumerable, it will try to cast the 
        /// value of the field to T and yield the result.
        /// </typeparam>
        /// <param name="fieldName">The name of the target field.</param>
        /// <returns>The enumerated values of the field.</returns>
        IEnumerable<T> EnumerateField<T>(string fieldName);

        /// <summary>
        /// Enumerates the fields tagged with a specified attribute, with all the fields converted to <typeparamref name="T"/>.
        /// If one of the field cannot be converted to <typeparamref name="T"/>, an exception is thrown.
        /// </summary>
        /// <typeparam name="T">The type that the target fields are converted into.</typeparam>
        /// <param name="attributeKey">The desired attribute. Pass <c>null</c> to select all fields in the cell.</param>
        /// <param name="attributeValue">Optionally specifies the value of the attribute. When <paramref name="attributeKey"/> is <c>null</c>, this parameter is ignored.</param>
        /// <returns>A list of key-value pairs representing the names and the values of the selected fields.</returns>
        IEnumerable<KeyValuePair<string, T>> SelectFields<T>(string attributeKey, string attributeValue = null);

        /// <summary>
        /// Enumerates the elements of the fields tagged with the specified attribute, with all the elements converted to <typeparamref name="T"/>.
        /// Each field satisfying the attribute conditions will be treated as a <c><![CDATA[System.Collections.Generic.IEnumerable<T>]]></c> of the desired type <typeparamref name="T"/>.
        /// All the elements are "flattened" and yielded into the result list so one cannot determine which field does an element come from.
        /// A single-value field that is compatible with type <typeparamref name="T"/> is also treated as a <c><![CDATA[System.Collections.Generic.IEnumerable<T>]]></c> collection.
        /// If the source of a value needs to be tracked, consider using <see cref="Trinity.Storage.ICell.SelectFields{T}(string, string)"/>.
        /// </summary>
        /// <typeparam name="T">The type to which the target elements are converted. If an element cannot be implicitly converted, an automatic conversion will be attempted.</typeparam>
        /// <param name="attributeKey">The desired attribute. Pass <c>null</c> to select all fields in the cell.</param>
        /// <param name="attributeValue">Optionally, specifies the value of the attribute. When <paramref name="attributeKey"/> is <c>null</c>, this parameter is ignored.</param>
        /// <returns>A collection of elements matching the specified attribute.</returns>
        IEnumerable<T> EnumerateValues<T>(string attributeKey, string attributeValue = null);

        /// <summary>
        /// Converts a runtime object representation, backed by the 
        /// cell type class, to a serialized buffer representation.
        /// </summary>
        /// <returns>The newly allocated & serialized accessor.</returns>
        ICellAccessor Serialize(); // XXX currently only guarantees readable. Writing causes undefined behaviour.
    }

    /// <summary>
    /// Exposes a group of generic methods to manipulate a cell via its accessor. All cell accessors generated from TSL implements <c>ICellAccessor</c>.
    /// </summary>
    public interface ICellAccessor : IAccessor, ICell, IDisposable
    {
        /// <summary>
        /// Converts the serialized buffer representation in the accessor,
        /// to a deserialized runtime object, backed by the cell type class.
        /// </summary>
        /// <returns></returns>
        ICell Deserialize();
    }

    /// <summary>
    /// Provides the type information for the data types.
    /// </summary>
    public interface ITypeDescriptor
    {
        /// <summary>
        /// The name of the type.
        /// </summary>
        string TypeName { get; }

        /// <summary>
        /// The CLR type descriptor.
        /// </summary>
        Type Type { get; }

        /// <summary>
        /// Indicates whether the type is of type <typeparamref name="T"/>.
        /// </summary>
        /// <typeparam name="T">The type to query.</typeparam>
        /// <returns>true if the type of the field matches the query type <typeparamref name="T"/>.</returns>
        bool IsOfType<T>();

        /// <summary>
        /// Indicates whether the field is a list.
        /// </summary>
        /// <returns>true if the field is a list.</returns>
        bool IsList();
    }

    /// <summary>
    /// Provides a read-only collection of attributes associated with a cell, a struct, or a field defined in TSL.
    /// </summary>
    public interface IAttributeCollection
    {
        /// <summary>
        /// The attribute collection.
        /// </summary>
        IReadOnlyDictionary<string, string> Attributes { get; }

        /// <summary>
        /// Gets the value of an attribute.
        /// </summary>
        /// <param name="attributeKey">The specified attribute key.</param>
        /// <returns><c>null </c>if not found.</returns>
        string GetAttributeValue(string attributeKey);
    }

    /// <summary>
    /// Provides metadata associated with a field defined in TSL.
    /// </summary>
    public interface IFieldDescriptor : ITypeDescriptor, IAttributeCollection
    {
        /// <summary>
        /// The name of the field.
        /// </summary>
        string Name { get; }

        /// <summary>
        /// <c>true</c> if the field is marked as <c>optional</c>.
        /// </summary>
        bool Optional { get; }
    }
}
